package org.infinitest.intellij.idea.language;

import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.openapi.editor.Document;
import org.apache.log4j.Logger;
import org.infinitest.TestEvent;

public class IdeaElement {
  private static Logger log = Logger.getLogger(IdeaElement.class);

  private PsiElement psiElement;

  IdeaElement(PsiElement psiElement) {
    this.psiElement = psiElement;
  }

  public static IdeaElement element(PsiElement element) {
    return new IdeaElement(element);
  }

  public boolean matches(TestEvent event) {
    return isMethodInClass(event.getPointOfFailure().getClassName()) &&
            isAtLine(event.getPointOfFailure().getLineNumber());
  }

  @SuppressWarnings({"ConstantConditions"})
  private boolean isMethodInClass(String className) {
    if (!(psiElement instanceof PsiMethodCallExpression))
    {
      return false;
    }
    PsiClass psiClass = findContainingClass(psiElement);
    return psiClass != null && psiClass.getQualifiedName().equals(className);
  }

  /**
   * Locates the class containing the provided element
   *
   * @param element element to find parent of
   * @return containing class or null if there is none
   */
  private PsiClass findContainingClass(PsiElement element) {
    if (element instanceof PsiMethod)
    {
      return ((PsiMethod) element).getContainingClass();
    }
    PsiElement parent = element.getParent();
    if (parent == null)
    {
      log.info(element + " has null parent");
      return null;
    }
    return findContainingClass(parent);
  }

  private boolean isAtLine(int line) {
    PsiFile file = psiElement.getContainingFile();
    Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
    return document != null && document.getLineNumber(psiElement.getTextOffset()) + 1 == line;
  }
}
